package com.molichuxing.services.aspect;

import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

/**
 * @prouect_name: order-service
 * @class_name: ${CLASS_NAME}
 * @description: ${DESCRIPTION}
 * @author: 林亿建
 * @create_date: 2019年01月09日
 * @modify_date: 2019年01月09日
 **/
@Component("globalAspect")
@Aspect
public class GlobalAspect {
	private static final Logger logger = LoggerFactory.getLogger(GlobalAspect.class);

	/**
	 * @within(org.springframework.stereotype.Service)，拦截带有 @Service 注解的类的所有方法
	 */
	@Pointcut("@within(org.springframework.stereotype.Service) && execution(public * *(..))")
	private void servicePointcut() {
	}

	/**
	 * 拦截service层异常，记录异常日志，并设置对应的异常信息 目前只拦截Exception，是否要拦截Error需再做考虑
	 *
	 * @param e
	 *            异常对象
	 */
	// @AfterThrowing(pointcut = "servicePointcut()", throwing = "e")
	// public void afterThrowing(JoinPoint joinPoint, Exception e) {
	// try {
	// String method = joinPoint.getSignature().toString();
	// List args = Arrays.asList(joinPoint.getArgs());
	// Map<String, Object> argsValue = getArgsNameValue(joinPoint);
	// LOGGER.error("调用方法:{0},参数：{1},参数值:{2},发生异常，异常信息:{3}", method, args,
	// argsValue.toString(),
	// e.fillInStackTrace());
	// throw new RuntimeException(e.getMessage());
	// } catch (NotFoundException ex) {
	// LOGGER.error(ex.getMessage());
	// throw new RuntimeException("服务提供者的服务实现AOP错误,无法修复");
	// }
	// }

	@Around("servicePointcut()")
	public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
		String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
		// List<Object> args = Arrays.asList(joinPoint.getArgs());
		Map<String, Object> argsValue = getArgsNameValue(joinPoint);
		logger.info("执行方法:{}, 参数值为:{} > 开始", methodName, argsValue);

		// 执行目标方法
		// AtomicReference<Object> result = new AtomicReference<>();
		Object obj = null;
		try {
			obj = joinPoint.proceed();
			// result.set(proceedingJoinPoint.proceed());
			logger.info("执行方法{}, 返回结果为:{}", methodName, obj);
		} catch (NotFoundException ex) {
			logger.error(ex.getMessage());
			throw new RuntimeException("服务提供者的服务实现AOP错误, 无法修复");
		} finally {
			logger.info("执行方法:{} > 结束", methodName);
		}

		return obj;

	}

	private Map<String, Object> getArgsNameValue(JoinPoint joinPoint) throws NotFoundException {
		String clazzName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] args = joinPoint.getArgs();
		if (args.length == 0) {
			return null;
		}

		ClassPool pool = ClassPool.getDefault();
		ClassClassPath classPath = new ClassClassPath(this.getClass());
		pool.insertClassPath(classPath);
		CtClass cc = pool.get(clazzName);
		CtMethod cm = cc.getDeclaredMethod(methodName);
		MethodInfo methodInfo = cm.getMethodInfo();
		CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		Map<String, Object> map = new HashMap<>();
		int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
		for (int i = 0; i < cm.getParameterTypes().length; i++) {
			try {
				map.put(attr.variableName(i + pos), args[i]); // paramNames即参数名
			} catch (Exception e) {

			}
		}

		return map;
	}

}
